﻿using FantasticMusicPlayer.dbo.Model;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FantasticMusicPlayer
{
    class CurrentDirectorySongProvider : IPlayListProvider
    {
        private string rootPath;

        private List<PlayList> unfilteredPlaylist;
        private Dictionary<string,AudioLoudnessEntry> loudnessEntries;

        public CurrentDirectorySongProvider() {
            rootPath = Path.GetFullPath(".");
            loudnessEntries = ReadLoudnessConfigFile();
            EnumrateDir(rootPath);
            unfilteredPlaylist = _playlists;
            _playlists = unfilteredPlaylist.Where(p => p.Songs.Count > 0).ToList();
            if(_playlists.Count == 0)
            {
                System.Windows.Forms.MessageBox.Show("当前文件夹下没有找到音乐文件。");
                Environment.Exit(1);
            }


        }
        const string configFileName = ".loudness.cfg";
        public Dictionary<string, AudioLoudnessEntry> ReadLoudnessConfigFile()
        {
            var entries = new Dictionary<string, AudioLoudnessEntry>();
            var configFilePath = Path.Combine(rootPath, configFileName);


            if (!File.Exists(configFilePath))
            {
                return new Dictionary<string, AudioLoudnessEntry>();
            }

            foreach (var line in File.ReadLines(configFilePath))
            {
                if (string.IsNullOrWhiteSpace(line))
                {
                    continue; // Skip empty lines
                }

                var parts = line.Split('|');
                if (parts.Length != 4)
                {
                    Console.WriteLine("Invalid line format in loudness configuration file.");
                    continue;
                }
                try { 
                    var entry = new AudioLoudnessEntry
                    {
                        RelativePath = parts[0],
                        PeakDB = float.Parse(parts[1], CultureInfo.InvariantCulture),
                        LUFS = float.Parse(parts[2], CultureInfo.InvariantCulture),
                    };
                    entries.Add(entry.RelativePath, entry);
                } catch (Exception ex) {
                    Console.WriteLine("Error while read line "+line);
                    Console.WriteLine(ex.ToString());
                }

            }

            return entries;
        }
        public class AudioLoudnessEntry
        {
            public string RelativePath;
            public float PeakDB;
            public float LUFS;
            public DateTime FileModified;
        }
        List<String> availableExtenstions = new List<string>(new string[] {".mp3",".wav",".flac",".ape",".aac"});

        public void EnumrateDir(String root) {
            if (root.Contains("/.") || root.Contains("\\."))
            {
                return;
            }
            PlayList pl = new PlayList(Path.GetFileName(root));
            PlayLists.Add(pl);
            Directory.EnumerateFiles(root, "*.pl").OrderBy(o => o).ToList().ForEach(f => SetupVirtualList(f));
            Directory.EnumerateFiles(root).Where(o => availableExtenstions.Any(a => o.ToLower().EndsWith(a))).OrderBy(o => o).ToList().ForEach(f => pl.Songs.Add(createSongEntry(f)));
            Directory.EnumerateDirectories(root).OrderBy(o => o).ToList().ForEach(o => EnumrateDir(o));
        }

        private SongEntry createSongEntry(string f)
        {
            var se = new SongEntry(f);
            var rp = se.getRelativePath();
            if (loudnessEntries.ContainsKey(rp))
            {
                var entry = loudnessEntries[rp];
                se.Loudness = new SongEntry.LoudnessInfo { LUFS = entry.LUFS, PeakDB = entry.PeakDB };
            }
            return se;
        }

        public void UpdatePlaylist() {
            _playlists = unfilteredPlaylist.Where(p => p.Songs.Count > 0).ToList();
        }

        public void SetupVirtualList(String virtualList) {
            string name = Path.GetFileNameWithoutExtension(virtualList);
            PlayLists.Add(new VirtualDir(name, virtualList, rootPath,this.loudnessEntries));
        }

        private List<PlayList> _playlists = new List<PlayList>();
        public List<PlayList> PlayLists => _playlists;

        public PlayList LastPlayList { get; set ; }
        public SongEntry LastSong { get ; set ; }

        public void loadProgress()
        {
            int folderpos = 0;
            int songpos = 0;
            if (File.Exists("point.conf")) {
                try
                {
                    String[] content = File.ReadAllLines("point.conf");
                    folderpos = int.Parse(content[0]);
                    songpos = int.Parse(content[1]);
                }
                catch { }
            }

            LastPlayList = PlayLists[Math.Min(folderpos, PlayLists.Count-1)];
            LastSong = LastPlayList.Songs[Math.Min(songpos, LastPlayList.Songs.Count - 1)];
        }

        public void saveProgress()
        {
            File.WriteAllLines("point.conf", new string[] { PlayLists.IndexOf(LastPlayList)+"",LastPlayList.Songs.IndexOf(LastSong)+"" });
        }
    }

    class VirtualDir : PlayList {
        private string fileName;
        private DateTime lastmodified = DateTime.MinValue;
        private string rootdir;
        private List<SongEntry> songs = new List<SongEntry>();
        private Dictionary<string, CurrentDirectorySongProvider.AudioLoudnessEntry> loudnessEntries;
        public VirtualDir(string name,string file,string root,Dictionary<string,CurrentDirectorySongProvider.AudioLoudnessEntry> loudnessData = null) : base(name) {
            fileName = file;
            rootdir = root;
            this.loudnessEntries = loudnessData;
            checkUpdate();
        }

        void checkUpdate() {
            if (File.GetLastWriteTime(fileName) != lastmodified) {
                songs.Clear();
                songs.AddRange(File.ReadAllLines(fileName).Where(f => File.Exists(Path.Combine(rootdir, f))).Select(f => createSongEntry(Path.Combine(rootdir, f.Replace('/',Path.DirectorySeparatorChar)))));
                Console.WriteLine("Loaded playlist " + Name + " with " + songs.Count + " songs.");
                lastmodified = File.GetLastWriteTime(fileName);
            }
        }
        private SongEntry createSongEntry(string f)
        {
            var se = new SongEntry(f);
            var rp = se.getRelativePath();
            if (loudnessEntries != null) {
                if (loudnessEntries.ContainsKey(rp))
                {
                    var entry = loudnessEntries[rp];
                    se.Loudness = new SongEntry.LoudnessInfo { LUFS = entry.LUFS, PeakDB = entry.PeakDB };
                }
            }
            
            return se;
        }

        public static string getReletivePath(SongEntry se)
        {
            String path = Path.GetFullPath(se.Path);
            path = path.Replace(Path.GetFullPath("."), "").Replace(Path.DirectorySeparatorChar, '/');
            if (path.StartsWith("/"))
            {
                path = path.Substring(1);
            }
            return path;
        }

        public void save() {
            File.WriteAllLines(fileName, songs.Select(s => getReletivePath(s)));
        }

        public override List<SongEntry> Songs { get { checkUpdate();return songs; } set { } }
    }
}
